package util

import (
	"crypto/rand"
	"fmt"
	"log"
	"reflect"
	"time"

	"cn.xdns.monitor/monitor-client/config"

	"strings"

	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

const (
	DateFormat = "2006-01-02"
	TimeFormat = "2006-01-02 15:04:05"
)

var DB *gorm.DB

type dbConf config.Conf

var dataConfig = dbConf(config.Config)

func dbUrl() func(config *dbConf) (dbType, url string) {
	dbType := "mysql"
	return func(config *dbConf) (string, string) {
		return dbType,
			fmt.Sprintf("%s:%s@%s(%s:%s)/%s?charset=utf8&parseTime=True&loc=Local",
				config.Mysql.Username,
				config.Mysql.Password,
				config.Mysql.Network,
				config.Mysql.Host,
				config.Mysql.Port,
				config.Mysql.Database)
	}
}

func init() {
	var err error
	DB, err = gorm.Open(dbUrl()(&dataConfig))
	if err != nil {
		panic(err)
	}

	// 禁用默认表名的复数形式，如果置为 true，则 `User` 的默认表名是 `user`
	DB.SingularTable(true)

	// 开启日志打印
	DB.LogMode(true)

	// 测试数据库连接是否正常
	Ping()

	//defer DB.Close()
}

// 生成 UUID
func UUID() string {
	defer func() {
		if err := recover(); err != nil {
			fmt.Printf("-->> generate uuid error, the message is : %v \n", err)
		}
	}()
	b := make([]byte, 16)
	_, err := rand.Read(b)
	if err != nil {
		log.Fatal(err)
	}
	uuid := fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])
	return strings.ReplaceAll(uuid, "-", "")
}

func Struct2Map(obj interface{}) *map[string]interface{} {
	t := reflect.TypeOf(obj) // 获取 obj 的类型信息
	v := reflect.ValueOf(obj)
	if t.Kind() == reflect.Ptr { // 如果是指针，则获取其所指向的元素
		t = t.Elem()
		v = v.Elem()
	}

	var data = make(map[string]interface{})
	if t.Kind() == reflect.Struct { // 只有结构体可以获取其字段信息
		for i := 0; i < t.NumField(); i++ {
			data[t.Field(i).Name] = v.Field(i).Interface()
		}

	}
	return &data
}

// 时间格式化
func TimeFomat(date *time.Time) string {
	if date != nil {
		return date.Format("2006-01-02 15:04:05")
	}
	return ""
}

// 自定义时间类型, 实现json化时间自定义
type Time time.Time

func Now() Time {
	return Time(time.Now())
}

func (t *Time) UnmarshalJSON(data []byte) (err error) {
	now, err := time.ParseInLocation(`"`+TimeFormat+`"`, string(data), time.Local)
	*t = Time(now)
	return
}

func (t Time) MarshalJSON() ([]byte, error) {
	b := make([]byte, 0, len(TimeFormat)+2)
	b = append(b, '"')
	b = time.Time(t).AppendFormat(b, TimeFormat)
	b = append(b, '"')
	return b, nil
}

func (t Time) String() string {
	return time.Time(t).Format(TimeFormat)
}

// 查看数据库能否连接
func Ping() {
	fmt.Println("testing")
	if err := DB.DB().Ping(); err != nil {
		fmt.Printf("=====> 数据库连接异常, 异常信息为: %v", err)
	} else {
		fmt.Printf("=====> 恭喜, 数据库连接成功!!! \n")
	}
}

// 事物操作
func TX(db *gorm.DB, data interface{}, action func(tx *gorm.DB) (int64, error)) (res int64, err error) {
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("-->> Recovered, the error msg is : ", r)
		}
	}()
	// 开始事务
	tx := db.Begin()
	// 在事务中做一些数据库操作 (这里应该使用 'tx' ，而不是 'db')
	if res, err = action(tx); err != nil {
		// 有错误时，手动调用事务的 Rollback()
		tx.Rollback()
		fmt.Println("=====> db opt error , the msg is :", err.Error())
		res = 0
	} else {
		// 无错误时，手动调用事务的 Commit()
		tx.Commit()
	}
	return res, err
}
